package com.aruba.andfixapplication;

import android.content.Context;
import android.os.Build;

import java.io.File;
import java.io.IOException;
import java.lang.reflect.Method;
import java.util.Enumeration;

import dalvik.system.DexFile;

/**
 * Created by aruba on 2020/4/30.
 */
public class DexManager {
    private Context mContext;

    private static final DexManager ourInstance = new DexManager();

    public static DexManager getInstance() {
        return ourInstance;
    }

    private DexManager() {
    }

    public void setContext(Context mContext) {
        this.mContext = mContext;
    }

    public void loadDexFile(File file) {
        try {
            DexFile dexFile = DexFile.loadDex(file.getAbsolutePath(),
                    new File(mContext.getCacheDir(), "fix.dex").getAbsolutePath(),
                    Context.MODE_PRIVATE);
            //类名迭代器
            Enumeration<String> entry = dexFile.entries();
            while (entry.hasMoreElements()) {
                String className = entry.nextElement();
                //Class.forName()获取不到，因为没有加载，我们需要手动加载
                Class clazz = dexFile.loadClass(className, mContext.getClassLoader());
                if (clazz != null) {
                    //开始修复
                    fixClass(clazz);
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    private void fixClass(Class clazz) {
        //获取class中的方法
        Method[] methods = clazz.getDeclaredMethods();
        for (int i = 0; i < methods.length; i++) {
            //获取注解
            Replace replace = methods[i].getAnnotation(Replace.class);
            if (replace == null) continue;

            //获取注解中需要被修复的方法和类
            String bugClazzStr = replace.clazz();
            String bugMethodStr = replace.method();

            try {
                Class bugClazz = Class.forName(bugClazzStr);
                //错误的方法
                Method bugMethod = bugClazz.getMethod(bugMethodStr, methods[i].getParameterTypes());
                if (Build.VERSION.SDK_INT <= 19) {//18版本以下，dalvik
                    replace(Build.VERSION.SDK_INT, bugMethod, methods[i], clazz);
                } else if (Build.VERSION.SDK_INT == 24) {
                    replaceArt7(bugMethod, methods[i]);
                } else {
                    replaceArt(bugMethod, methods[i]);
                }
            } catch (ClassNotFoundException e) {
                e.printStackTrace();
            } catch (NoSuchMethodException e) {
                e.printStackTrace();
            }
        }
    }

    private native void replace(int sdk, Method wrongMethod, Method rightMethod, Class rightClazz);

    private native void replaceArt(Method wrongMethod, Method rightMethod);

    private native void replaceArt7(Method wrongMethod, Method rightMethod);
}
